package com.cardone.security.token.util;

import com.cardone.common.cache.util.CacheUtils;
import com.cardone.common.util.WebUtils;
import com.cardone.context.CodeException;
import com.cardone.context.action.Run1Action;
import com.cardone.context.function.Execution1Function;
import com.cardone.security.token.dto.TokenDto;
import com.google.common.collect.Maps;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.util.Map;
import java.util.UUID;

/**
 * Created by yaohaitao on 15-1-13.
 */
public class TokenUtils {
    @Setter
    private static Execution1Function<TokenDto, Map<String, Object>> generateFunction = stringObjectMap -> {
        String clientId = MapUtils.getString(stringObjectMap, "client_id");

        TokenDto token = new TokenDto();

        token.setId(MapUtils.getString(stringObjectMap, "id"));
        token.setClientId(MapUtils.getString(stringObjectMap, "client_id"));
        token.setKey(StringUtils.remove(UUID.randomUUID().toString(), "-"));

        TokenUtils.remove(clientId, null);
        CacheUtils.put(TokenUtils.class.getName(), token.getKey(), token);

        return token;
    };

    @Setter
    private static Execution1Function<TokenDto, Map<String, Object>> generateForOldTokenFunction = stringObjectMap -> {
        String clientId = MapUtils.getString(stringObjectMap, "client_id");
        String oldTokenKey = MapUtils.getString(stringObjectMap, "old_token_key");

        TokenDto token = TokenUtils.validate(clientId, oldTokenKey);

        token.setKey(StringUtils.remove(UUID.randomUUID().toString(), "-"));

        TokenUtils.remove(clientId, oldTokenKey);
        CacheUtils.put(TokenUtils.class.getName(), token.getKey(), token);

        return token;
    };

    @Setter
    private static Execution1Function<TokenDto, Map<String, Object>> generateForUrlFunction = stringObjectMap -> {
        String clientId = MapUtils.getString(stringObjectMap, "client_id");
        String url = MapUtils.getString(stringObjectMap, "url");

        TokenDto token = new TokenDto();

        token.setId(MapUtils.getString(stringObjectMap, "id"));
        token.setClientId(clientId);
        token.setKey(StringUtils.remove(UUID.randomUUID().toString(), "-"));
        token.setUrl(url);

        CacheUtils.put(TokenUtils.class.getName(), token.getKey(), token);

        return token;
    };

    @Setter
    private static Execution1Function<TokenDto, String> getFunction = tokenKey -> CacheUtils.get(TokenDto.class, TokenUtils.class.getName(), tokenKey);

    @Setter
    private static Run1Action<Map<String, Object>> removeAction = stringObjectMap -> {
        String oldTokenKey = MapUtils.getString(stringObjectMap, "old_token_key");

        CacheUtils.evict(TokenUtils.class.getName(), oldTokenKey);
    };

    @Setter
    private static Execution1Function<TokenDto, Map<String, Object>> validateFunction = stringObjectMap -> {
        String clientId = MapUtils.getString(stringObjectMap, "client_id");
        String oldTokenKey = MapUtils.getString(stringObjectMap, "old_token_key");

        TokenDto token = TokenUtils.get(oldTokenKey);

        if (!StringUtils.equals(clientId, token.getClientId())) {
            throw new CodeException("设备码不正确");
        }

        return token;
    };

    @Setter
    private static Execution1Function<TokenDto, Map<String, Object>> validateUrlFunction = stringObjectMap -> {
        String clientId = MapUtils.getString(stringObjectMap, "client_id");
        String oldTokenKey = MapUtils.getString(stringObjectMap, "old_token_key");

        TokenDto token = TokenUtils.get(oldTokenKey);

        if (!StringUtils.equals(clientId, token.getClientId())) {
            throw new CodeException("设备码不正确");
        }

        String url = MapUtils.getString(stringObjectMap, "url");

        if (com.cardone.common.util.StringUtils.matchs(token.getUrl(), url)) {
            throw new CodeException("未授权的请求");
        }

        return token;
    };

    public static TokenDto generate(String clientId, String id) {
        Map<String, Object> map = Maps.newHashMap();

        map.put("client_id", clientId);
        map.put("id", id);

        return generateFunction.execution(map);
    }

    public static TokenDto generate(String id) {
        Map<String, Object> map = Maps.newHashMap();

        String clientId = CacheUtils.get(String.class, WebUtils.getSessionId(), TokenUtils.getClientIdParamName());

        map.put("client_id", clientId);
        map.put("id", id);

        return generateFunction.execution(map);
    }

    public static String getTokenKey() {
        String tokenKey = CacheUtils.get(String.class, WebUtils.getSessionId(), TokenUtils.getTokenParamName());

        if (StringUtils.isNotBlank(tokenKey)) {
            return tokenKey;
        }

        return getTokenKeyByClientId();
    }

    public static String getTokenKeyByClientId() {
        String clientId = CacheUtils.get(String.class, WebUtils.getSessionId(), TokenUtils.getClientIdParamName());

        return getTokenKeyByClientId(clientId);
    }

    public static String getTokenKeyByClientId(String clientId) {
        if (StringUtils.isBlank(clientId)) {
            return null;
        }

        return CacheUtils.get(String.class, clientId, TokenUtils.getTokenParamName());
    }

    public static TokenDto generateForOldToken(String clientId, String oldTokenKey) {
        Map<String, Object> map = Maps.newHashMap();

        map.put("client_id", clientId);
        map.put("old_token_key", oldTokenKey);

        return generateForOldTokenFunction.execution(map);
    }

    public static TokenDto generateForUrls(String clientId, String oldTokenKey, String url) {
        Map<String, Object> map = Maps.newHashMap();

        map.put("client_id", clientId);
        map.put("old_token_key", oldTokenKey);
        map.put("url", url);

        return generateForUrlFunction.execution(map);
    }

    public static TokenDto get(String tokenKey) {
        return getFunction.execution(tokenKey);
    }

    public static void remove(String clientId, String oldTokenKey) {
        Map<String, Object> map = Maps.newHashMap();

        map.put("client_id", clientId);
        map.put("old_token_key", oldTokenKey);

        removeAction.run(map);
    }

    public static TokenDto validate(String clientId, String oldTokenKey) {
        Map<String, Object> map = Maps.newHashMap();

        map.put("client_id", clientId);
        map.put("old_token_key", oldTokenKey);

        return validateFunction.execution(map);
    }

    public static TokenDto validateUrl(String clientId, String oldTokenKey, String url) {
        Map<String, Object> map = Maps.newHashMap();

        map.put("client_id", clientId);
        map.put("old_token_key", oldTokenKey);
        map.put("url", url);

        return validateUrlFunction.execution(map);
    }

    @Getter
    @Setter
    private static String tokenParamName = "token";

    @Getter
    @Setter
    private static String clientIdParamName = "client_id";

    public static String getTokenKey(HttpServletRequest request, Map<String, Object> parameterMap) {
        return WebUtils.getParameterValue(request, parameterMap, tokenParamName);
    }

    public static String getClientId(HttpServletRequest request, Map<String, Object> parameterMap) {
        return WebUtils.getParameterValue(request, parameterMap, clientIdParamName);
    }
}
